iT邦幫忙

2023 iThome 鐵人賽

DAY 12
0
自我挑戰組

30天開啟.NET後端工程師的旅程系列 第 12

Day 12 單一職責原則 (Single Responsibility Principle)

  • 分享至 

  • xImage
  •  

前言

既然在Day10 剛好有提到單一職責原則,這裡就來簡單說明。
「單一職責」原則顧名思義,就是一個類別應該只負責一個職責(是SOLID原則其中一個原則)。


SOLID 是物件導向設計中的五個重要原則,這些原則旨在幫助開發人員創建更容易維護、擴展和理解的軟體。

SOLID 原則是下面這幾個原則的縮寫,每個原則讓我們用輕鬆的方式來說明:

  1. 單一職責原則(Single Responsibility Principle - SRP)

    就像小時候被教育應該專注負責做一件事一樣,一個物件或類別也應該只做一件事情。
    比如,如果你是負責畫圖的小畫家,你就專注畫圖就好,不要再去唱歌跳舞或是打遊戲。

  2. 開放/封閉原則(Open/Closed Principle - OCP)

    就像玩樂高積木,可以一直添加新的積木,但不需要改變已經建好的部分。
    你可以建立另外再加蓋新的城堡或汽車,而不需要破壞以前建好的東西。

  3. 里氏替換原則(Liskov Substitution Principle - LSP)

    如果有一個玩具火車,可以用不同的顏色或形狀的火車車廂來替換,但它們都應該能夠在軌道上運行,不應該有問題。這就像在使用不同的玩具部件時,不應該出現問題。

  4. 介面隔離原則(Interface Segregation Principle - ISP)

    這就像是玩具箱,可以選擇玩具,不需要使用所有的玩具。
    例如,你可以選擇用樂高積木來建城堡,而不使用汽車玩具。每個玩具都有自己的用途,不需要用到所有的。

  5. 依賴反轉原則(Dependency Inversion Principle - DIP)

    這就像想要玩遙控車,可以用一個遙控器來控制玩具,而不是直接抓車子本身去移動。
    遙控器是一個抽象的方式來控制不同的玩具,你可以改變遙控器上面的搖桿的方向,而不是直接移動車子本身。這樣更容易控制你的玩具,而不需要改變它們。

這些 SOLID 原則是物件導向軟體設計的基石,幫助開發人員建立高質量、可維護的程式碼。遵循這些原則有助於提高程式的可讀性、可擴展性和可維護性,並降低變更代碼時引入錯誤的風險。

單一職責原則 (Single Responsibility Principle)

下面使用Day 9 有提到的Method,來舉一些不遵循單一職責原則的例子,以及如何將它們拆分成單一職責的方法:

合併並排序列表: 假設有一個Method需要合併兩個不同的列表並對它們進行排序。
這個Method實際上執行了兩個不同的任務:合併和排序。
解決方法是將這兩個功能分別封裝到兩個不同的Method中,一個用於合併,另一個用於排序。這樣做的好處是提高代碼的可讀性、可維護性和可測試性。

// 不遵循單一職責原則
public List<int> MergeAndSortLists(List<int> list1, List<int> list2)
{
    var mergedList = list1.Concat(list2).ToList();
    return mergedList.OrderBy(x => x).ToList();
}

在這個Method中,它執行了兩個不同的任務:

  1. 合併:它將兩個列表 list1list2 合併成一個新的列表 mergedList
  2. 排序:它對 mergedList 列表進行排序。
// 遵循單一職責原則
public List<int> MergeLists(List<int> list1, List<int> list2)
{
    return list1.Concat(list2).ToList();
}

public List<int> SortList(List<int> list)
{
    return list.OrderBy(x => x).ToList();
}

在這個版本中,可以看到把合併和排序拆分成了兩個不同的Method:

  1. MergeLists Method接受兩個列表 list1list2 並返回它們的合併結果。
  2. SortList Method接受一個列表 list 並返回排序後的結果。

這樣的好處是,現在每個Method只負責一個特定的任務,更容易理解和測試。
如果以後需要修改合併邏輯或排序邏輯,只需要關注相應的Method,而不會影響到其他部分的代碼,而且這種方式也有助於提高代碼的可重用性,因為你也可以在其他地方使用這些函數來執行相同的操作。

再來舉其他例子看看。

  • 檢查並儲存檔案: 假設有一個Method需要檢查檔案是否存在,需要執行一個文件處理相關的操作,其中包括檢查文件是否存在並保存文件內容。
    不遵循單一職責原則的版本,可以看到Method執行了兩個不同的操作。

    // 不遵循單一職責原則
    public void CheckAndSaveFile(string filePath, string content)
    {
        if (File.Exists(filePath))
        {
            // 儲存檔案
            File.WriteAllText(filePath, content);
        }
    }
    

    在這個Method中,它執行了兩個不同的任務:

    1. 檢查文件是否存在:它使用 File.Exists(filePath) 檢查指定路徑的文件是否存在。
    2. 儲存文件內容:如果文件存在,它使用 File.WriteAllText(filePath, content) 來儲存文件內容。
// 遵循單一職責原則
public bool FileExists(string filePath)
{
    return File.Exists(filePath);
}

public void SaveFile(string filePath, string content)
{
    File.WriteAllText(filePath, content);
}

在這個版本中,我們將檢查文件是否存在和保存文件內容分為兩個不同的Method:

  1. FileExists Method接受文件路徑 filePath 並返回一個布林值,表示該文件是否存在。
  2. SaveFile Method接受文件路徑 filePath 和文件內容 content,並將內容儲存到指定的文件中。

身為.NET工程師,多數時間都需要與資料庫操作(把資料查找出來,新增修改或刪除)。

資料庫操作和業務邏輯混合: 在下面的例子裏面,涉及到更新使用者信息的操作,同時伴隨著業務邏輯,其中包括年齡檢查和通知。我們可以將這些操作拆分成兩個遵循單一職責原則的Method。

首先如果不遵循單一職責原則

// 不遵循單一職責原則
public void UpdateUserInformation(User user)
{
    // 資料庫更新操作
    database.UpdateUser(user);

    // 業務邏輯
    if (user.Age < 18)
    {
        SendNotification(user);
    }
}

在這個Method中,它同時執行了兩個不同的任務:

  1. 資料庫更新操作:使用 database.UpdateUser(user) 來更新使用者的資料庫信息。
  2. 業務邏輯:它檢查使用者的年齡是否小於 18 歲,如果是,則調用 SendNotification(user) 來發送通知。

那如果是改成遵循單一職責原則的版本的話

// 遵循單一職責原則
public void UpdateUserInformation(User user)
{
    // 資料庫更新操作
    database.UpdateUser(user);
}

public void NotifyIfUnderage(User user)
{
    // 業務邏輯
    if (user.Age < 18)
    {
        SendNotification(user);
    }
}

將資料庫更新操作和業務邏輯拆分為兩個不同的方法:

  1. UpdateUserInformation 這個Method負責執行資料庫的更新操作,並傳入使用者信息。
  2. NotifyIfUnderage 這個Method負責執行業務邏輯,檢查使用者的年齡是否小於 18 歲,如果是,則調用 SendNotification(user) 來發送通知。

各司其職,這樣分開的好處是如果以後需要修改資料庫更新邏輯或業務邏輯,只需要關注相應的Method,而不會影響到其他部分的程式碼。


第12天挑戰完成!!!


上一篇
Day 11 物件導向 OOP
下一篇
Day 13 關鍵字 - return , params
系列文
30天開啟.NET後端工程師的旅程30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言